using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Rotempco.Core.ORM.Logic;
using Rotempco.Core.ORM.Objects;
using Rotempco.Core.ORM.Events;
using Rotempco.Core.ORM.Interfaces;
using Rotempco.Core.ORM.Objects.Enums;
using Rotempco.Core.ORM.Objects.Stores;
using System.Threading;
using Rotempco.Core.ORM.Managers;

namespace Rotempco.Core.ORM.ADAL
{
    public abstract class ADAL<TDomainObject, TPrimaryKey> : ADALReadOnly<TDomainObject, TPrimaryKey>, IDAL<TDomainObject, TPrimaryKey>
        where TDomainObject : class, new()
        where TPrimaryKey : struct, IComparable
    {
        public event DataUpdatedDelegate DataUpdated;

        public virtual void WriteToDB(TDomainObject instance)
        {
            TPrimaryKey id = GetId(instance);

            if (id.Equals(default(TPrimaryKey)))
                Add(instance);
            else
                Update(instance, id);
        }

        public virtual void Delete(TPrimaryKey id, bool forceDelete = false)
        {
            string identifierColumnName = GetIdentityColumn();

            ORMParameter deletedParameter = DeletedParameter();

            if (deletedParameter != null && forceDelete == false)
            {
                List<SqlParameter> sqlParameterList = new List<SqlParameter>();
                sqlParameterList.Add(new SqlParameter("@" + identifierColumnName, id));
                sqlParameterList.Add(new SqlParameter("@DeletedDate", DateTime.Now));

                DB.ExecuteNonQuery(@"UPDATE [" + GetTableName() + "]" +
                                    @" SET [" + deletedParameter.PropertyName + "] = @DeletedDate" +
                                    " WHERE [" + identifierColumnName + "] = @" + identifierColumnName + "",
                                    sqlParameterList.ToArray());
            }
            else
            {
                DB.ExecuteNonQuery(@"DELETE FROM [" + GetTableName() + "]" +
                                    " WHERE [" + identifierColumnName + "] = @" + identifierColumnName + "",
                                    new SqlParameter("@" + identifierColumnName, id));
            }

            if (DataUpdated != null)
                DataUpdated(DataUpdatedTypeEnum.DataDeleted);
        }

        public void DeleteAll(bool forceDelete = false)
        {
            string identifierColumnName = GetIdentityColumn();

            ORMParameter deletedParameter = DeletedParameter();

            if (deletedParameter != null && forceDelete == false)
            {
                List<SqlParameter> sqlParameterList = new List<SqlParameter>();
                sqlParameterList.Add(new SqlParameter("@DeletedDate", DateTime.Now));

                DB.ExecuteNonQuery(@"UPDATE [" + GetTableName() + "] SET [" + deletedParameter.PropertyName + "] = @DeletedDate", sqlParameterList.ToArray());
            }
            else
            {
                DB.ExecuteNonQuery(@"DELETE FROM [" + GetTableName() + "]");
            }

            if (DataUpdated != null)
                DataUpdated(DataUpdatedTypeEnum.DataDeleted);
        }

        public virtual void Restore(TPrimaryKey id)
        {
            ORMParameter deletedParameter = DeletedParameter();
            string identifierColumnName = GetIdentityColumn();

            if (deletedParameter == null)
                throw new NotImplementedException("Restore is not implemented on domainobject: " + typeof(TDomainObject).Name);

            List<SqlParameter> sqlParameterList = new List<SqlParameter>();
            sqlParameterList.Add(new SqlParameter("@" + identifierColumnName, id));

            DB.ExecuteNonQuery(@"UPDATE [" + GetTableName() + "]" +
                                @" SET [" + deletedParameter.PropertyName + "] = null" +
                                " WHERE [" + identifierColumnName + "] = @" + identifierColumnName + "",
                                sqlParameterList.ToArray());

            if (DataUpdated != null)
                DataUpdated(DataUpdatedTypeEnum.DataDeleted);
        }


        # region [ Private methods ]

        private void Add(TDomainObject instance)
        {
            string tableName = GetTableName();
            List<ORMParameter> ormMapping = GetORMMapping();
            string identifierColumnName = GetIdentityColumn();

            List<string> columnNamesList;
            List<string> ormColumnValueParameterList;

            if (EncryptionStoreManager.Instance.IsEncryptionDisabled())
            {
                columnNamesList = (from o in ormMapping where o.EncryptionType == EncryptionTypeEnum.ClearText select o.ColumnName).ToList();
                ormColumnValueParameterList = (from o in ormMapping where o.EncryptionType == EncryptionTypeEnum.ClearText select o.ColumnName).ToList();
            }
            else
            {
                columnNamesList = (from o in ormMapping select o.ColumnName).ToList();
                ormColumnValueParameterList = (from o in ormMapping select o.ColumnName).ToList();
            }

            string ormColumnNames = string.Join(", ", columnNamesList);
            string ormColumnValueParameters = string.Join(",  @", ormColumnValueParameterList);


            string insertSql = @"INSERT INTO [" + tableName + "] (" + ormColumnNames + ") VALUES (@" + ormColumnValueParameters + "); SELECT @@identity as id;";

            TPrimaryKey id = DB.GetExecuteScalar<TPrimaryKey>(insertSql, GetSqlParameters(instance));
            SetId(instance, id);

            if (DataUpdated != null)
                DataUpdated(DataUpdatedTypeEnum.DataWriten);

        }

        private void Update(TDomainObject instance, object identifierValue)
        {
            string tableName = GetTableName();
            List<ORMParameter> ormMapping = GetORMMapping();
            string identifierColumnName = GetIdentityColumn();


            string updateSql = "UPDATE [" + tableName + "] SET ";
            foreach (ORMParameter ormParameter in ormMapping)
            {
                if (ormParameter.EncryptionType != EncryptionTypeEnum.ClearText && EncryptionStoreManager.Instance.IsEncryptionDisabled())
                    continue;

                updateSql += (ormParameter.ColumnName + @" = @" + ormParameter.ColumnName + ",");
            }
            updateSql = updateSql.Remove(updateSql.Length - 1, 1);

            updateSql += (" WHERE " + identifierColumnName + " = " + identifierValue);
            DB.ExecuteNonQuery(updateSql, GetSqlParameters(instance));

            if (DataUpdated != null)
                DataUpdated(DataUpdatedTypeEnum.DataWriten);
        }

        # endregion
    }
}
